/* 
 * File:   common.h
 * Author: rsetrav1
 *
 * Created on December 02, 2012, 4:11 PM
 */

#ifndef _COMMON_H
#define	_COMMON_H

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define NUM_SERVERS                         5

#define MAX_CHATROOMID_LEN                  100
#define MAX_MSG_LEN                         80
#define MAX_USERNAME_LEN                    20
#define MAX_SPREAD_GROUP_LEN                200
#define MAX_CLIENT_ID_LEN                   30
#define DEFAULT_NUM_MSGS                    25

/*********** SPREAD Message Types ***********/
#define CLIENT_TO_SERVER                    1
#define SERVER_TO_SERVER                    2
#define SERVER_TO_CLIENT_GROUP              3
#define SERVER_TO_SERVER_P                  4
#define SERVER_TO_CLIENT                    5
#define SERVER_TO_CLIENT_DEBUG              6

/*********** UserMsg Types ***********/
#define USERMSG_USERNAME                    1
#define USERMSG_JOIN                        2
#define USERMSG_LEAVE                       3
#define USERMSG_APPEND                      4
#define USERMSG_LIKE                        5
#define USERMSG_UNLIKE                      6
#define USERMSG_HISTORY                     7
#define USERMSG_VIEW                        8

/* Group Prefixes Shared with Clients */
#define SERVER_PRIVATE_GROUP "G_SERVER_"
#define SERVER_CLIENT_ALL "G_CLIENTALL_SERVER_"


/*********** Common Structures used in Messages ***********/
struct LamportTimeStamp {
    int             serverId;  /* Indicates the server from which the message originated */
    int             localId;
    int             globalId; /* TODO: TimeStamp */
};
typedef struct LamportTimeStamp LTS;

struct ChatRoomInfo {
    char            chatRoom[MAX_CHATROOMID_LEN];
    int             numUsers;
/*  (List of  <<UserName><ClientId>>)
/*  char            *userName[MAX_USERNAME_LEN]; == List of users */
};
typedef struct ChatRoomInfo ChatRoomInfo;

struct ChatMsgInfo {
    LTS             lts;
    char            userName[MAX_USERNAME_LEN];
    char            msg[MAX_MSG_LEN];
    int             numLikes;
/*  TODO: Check this? Also optionally includes the list of users who likes this message */
};
typedef struct ChatMsgInfo ChatMsgInfo;

struct ServerInfo {
    int             serverId;
    LTS             highestLts;
};
typedef struct ServerInfo ServerInfo;

/*********** Message Types ***********/

/**** Normal Case ****/
/* Type#1: Client to Server */
struct UserMsg {
    int             type; /*   of type USERMSG_*   */
    char            chatRoom[MAX_CHATROOMID_LEN];
    char            msg[MAX_MSG_LEN];           /* LTS in case of LIKE/UNLIKE */
    char            userName[MAX_USERNAME_LEN];
};
typedef struct UserMsg UserMsg;


/* Type#2&3: Server to Server & ClientGroup */
struct ServerMsg {
    LTS             lts;
    UserMsg         userMsg;
    char            clientId[MAX_CLIENT_ID_LEN];
};
typedef struct ServerMsg ServerMsg;


/**** Healing Case ****/
/* Type#4: Server to Server - when partion change occurs */
struct ServerPartitonMsgHdr {
    int             serverId;
    int             localIdVector[NUM_SERVERS];
    int             numChatRooms;
/*  List of (ChatRoomInfo, list of users) */
};
typedef struct ServerPartitonMsgHdr ServerPartitonMsgHdr;


/* Type#5: Server to Client
 * - when partition change occurs
 * OR when a new user joins a chat room to this Server.
 * OR when user wants to see all messages in a chat room */
struct ClientMsgHdr {
    int             numUsers;
    int             numOfChatMsgs;
    /* List of users */
    /* List of ChatMsgInfo */
};
typedef struct ClientMsgHdr ClientMsgHdr;


/* Type#6: Server to Client - For viewing the state at each server */
struct ServerView {
    ServerInfo serverInfo[NUM_SERVERS];
/*  List of (ChatRoomInfo, list of users) */
};

/* G_CharRoomName_ServerID */
void constructChatRoomGroupName(char *chatRoom, char* retGroupName, int serverId) {
    char            serverIdStr[10];
    sprintf(serverIdStr, "%d\0", serverId); /* Should always use only the client groups that are connected to this server */

    strcpy(retGroupName, "G_");
    strcat(retGroupName, chatRoom);
    strcat(retGroupName, "_");
    strcat(retGroupName, serverIdStr);
}

void printLTS(LTS *lts) {
    if(lts != NULL) {
        printf("[%d %d %d]", lts->globalId, lts->localId, lts->serverId);
    } else {
        printf("LTS is NULL\n");
    }
}

void printUserMsg(UserMsg *userMsg) {
    if(userMsg != NULL) {
        switch(userMsg->type) {
            case USERMSG_JOIN:
                printf("[%s]", "JOIN");
                break;
            case USERMSG_LEAVE:
                printf("[%s]", "LEAVE");
                break;
            case USERMSG_APPEND:
                printf("[%s]", "APPEND");
                break;
            case USERMSG_LIKE:
                printf("[%s]", "LIKE");
                break;
            case USERMSG_UNLIKE:
                printf("[%s]", "UNLIKE");
                break;
        }
        printf(" [%s] [%s] ", userMsg->chatRoom, userMsg->userName);
        if(userMsg->type == USERMSG_LIKE || userMsg->type == USERMSG_UNLIKE) {
            printLTS((LTS*)userMsg->msg);
        } else {
            printf("[%s]", userMsg->msg);
        }
    } else {
        printf("UserMsg is NULL\n");
    }
    printf("\n");
}

void printServerMsg(ServerMsg* serverMsg) {
    if(serverMsg != NULL) {
        printf("[%s] ", serverMsg->clientId);
        printLTS(&(serverMsg->lts));
        printUserMsg(&serverMsg->userMsg);
    } else {
        printf("ServerMsg is NULL\n");
    }
}

void printChatMsg(ChatMsgInfo *chatMsgInfo) {
    if(chatMsgInfo != NULL) {
        printLTS(&chatMsgInfo->lts);
        printf("[%s] [%s] [%d]\n", chatMsgInfo->userName, chatMsgInfo->msg, chatMsgInfo->numLikes);
    } else {
        printf("ChatMsgInfo is NULL\n");
    }
}

#endif	/* _COMMON_H */
